﻿
namespace KinonekoSoftware.Studio.Pages.Galleries.Geography
{
    public class GeographyGalleryViewModel : RepositoryGalleryItem
    {
        private readonly InstanceCacheEngine               _Engine_Cache;
        private readonly List<InstanceCache>               _FullyCollection;
        private readonly Dictionary<string, InstanceCache> _Map;

        private int           _version;
        private string        _searchBy;
        private InstanceCache _currentSelected;


        public GeographyGalleryViewModel()
        {
            _Map             = new Dictionary<string, InstanceCache>();
            Collection       = new ObservableCollection<InstanceCache>();
            _FullyCollection = new List<InstanceCache>(128);
            _Engine_Cache    = Platform.GetEngine<InstanceCacheEngine>();


            AddGeographyCommand          = CreateCommand<InstanceCache>(DoAddGeographyCommand);
            RenameGeographyCommand       = CreateCommand<InstanceCache>(DoRenameGeographyCommand);
            EditGeographyCommand         = CreateCommand<InstanceCache>(DoEditGeographyCommand);
            ShiftUpGeographyCommand      = CreateCommand<InstanceCache>(DoShiftUpGeographyCommand);
            ShiftDownGeographyCommand    = CreateCommand<InstanceCache>(DoShiftDownGeographyCommand);
            OpenNotionCommand            = CreateCommand<InstanceCache>(DoOpenNotionCommand);
            OpenStoryCommand             = CreateCommand<InstanceCache>(DoOpenStoryCommand);
            LockOrUnlockGeographyCommand = CreateCommand<InstanceCache>(DoLockOrUnlockGeographyCommand);
            RemoveGeographyCommand       = CreateCommand<InstanceCache>(DoRemoveGeographyCommand);
        }

        private void Rebuild()
        {
            //
            //
            _Map.Clear();

            var pending = new Queue<InstanceCache>();

            //
            // First 
            foreach (var cache in _FullyCollection)
            {
                //
                // add cache to map
                _Map.TryAdd(cache.Id, cache);

                //
                // assign 999999999 to keep always initialized value was min
                cache.MinIndex = 999999999;
            }


            var min = 999999999;
            var max = 0;

            //
            // second
            foreach (var cache in _FullyCollection)
            {
                if (string.IsNullOrEmpty(cache.ParentID))
                {
                    cache.Children ??= new ObservableCollection<InstanceCache>();
                    Collection.Add(cache);


                    min = Math.Min(min, cache.Index);
                    max = Math.Max(max, cache.Index);
                }
                else if (_Map.TryGetValue(cache.ParentID, out var parent))
                {
                    parent.Children ??= new ObservableCollection<InstanceCache>();
                    pending.Enqueue(cache);
                    parent.Children.Add(cache);
                    parent.MinIndex = Math.Min(parent.MinIndex, cache.Index);
                    parent.MaxIndex = Math.Max(parent.MaxIndex, cache.Index);
                }
                else
                {
                    cache.Children ??= new ObservableCollection<InstanceCache>();
                    cache.ParentID =   null;
                    Collection.Add(cache);
                    min = Math.Min(min, cache.Index);
                    max = Math.Max(max, cache.Index);
                }
            }

            while (pending.Count > 0)
            {
                //
                //
                var parent = pending.Dequeue();

                if(parent.Children is null || 
                    parent.Children.Count == 0)
                {
                    continue;
                }
                //
                //
                var count = parent.Children.Count;

                //
                // n * (a1 + an) / 2
                var desiredResult = count * (0 + count       - 1)               / 2;
                var actualResult  = count * (parent.MinIndex + parent.MaxIndex) / 2;

                if (actualResult == desiredResult)
                {
                    var array = parent.Children.ToArray();
                    Array.Sort(array);
                    parent.Children.Clear();
                    parent.Children.AddMany(array);
                }
                else
                {
                    for (var i = 0; i < count; i++)
                    {
                        var item = parent.Children[i];
                        item.Index = i;
                        _Engine_Cache.UpdateCache(item);
                    }
                }
            }


            RebuildRoot(min, max);
        }

        private void RebuildRoot(int min, int max)
        {
            //
            //
            var count = Collection.Count;

            //
            // n * (a1 + an) / 2
            var desiredResult = count * (0 + count - 1)   / 2;
            var actualResult  = count * (min       + max) / 2;

            if (actualResult == desiredResult)
            {
                var array = Collection.ToArray();
                Array.Sort(array);
                Collection.Clear();
                Collection.AddMany(array);
            }
            else
            {
                for (var i = 0; i < count; i++)
                {
                    var item = Collection[i];
                    item.Index = i;
                    _Engine_Cache.UpdateCache(item);
                }
            }
        }

        private async void Initialize()
        {
            using (var session = await Ioc.Get<IBusyServiceEX>().BeginSession("正在加载数据..."))
            {
                //
                //
                _FullyCollection.AddMany(_Engine_Cache.GetGeographies(), true);

                //
                //
                Collection.Clear();

                //
                // 重建顺序
                Rebuild();

                //
                //
                await session.Await();
            }

            _version = _Engine_Cache.Version;
        }

        protected override void OnStart()
        {
            Initialize();
            base.OnStart();
        }

        protected override void OnResume()
        {
            if (_version != _Engine_Cache.Version)
            {
                Initialize();
            }
            base.OnResume();
        }


        #region CommandHandlers

        private async void DoAddGeographyCommand(InstanceCache parent)
        {
            var r = await SingleText("新建地理设定", "地理设定的名称");

            if (!r.IsFinished)
            {
                return;
            }

            //
            // 
            var cache = DataFactory.Create(InstanceType.Geography, r.Value);
            
            
            //
            //
            cache.Depth = 1;
            

            if (parent is null)
            {
                //
                //
                cache.Index = Collection.Count;
                Collection.Add(cache);
            }
            else if(parent.Depth <= Codaria.MaxGeographyDepth)
            {
                parent.Children ??= new ObservableCollection<InstanceCache>();
                //
                //
                cache.Index    = parent.Children.Count;
                cache.ParentID = parent.Id;
                cache.Depth    = parent.Depth + 1;

                //
                //
                parent.Children.Add(cache);
            }

            //
            // 添加 Cache
            _Map.Add(cache.Id, cache);
            _Engine_Cache.AddCache(cache);
        }


        private void DoOpenNotionCommand(InstanceCache element)
        {
            this.ThisStory(element);
        }


        private void DoOpenStoryCommand(InstanceCache element)
        {
            this.ThisStory(element);
        }


        private void DoShiftUpGeographyCommand(InstanceCache element)
        {
            if (element is null)
            {
                return;
            }

            if (string.IsNullOrEmpty(element.ParentID))
            {
                var count = Collection.Count;
                Collection.ShiftUp(element);

                for (var i = 0; i < count; i++)
                {
                    var item = Collection[i];
                    if (item.Index != i)
                    {
                        item.Index = i;
                        _Engine_Cache.UpdateCache(item);
                    }
                }
            }
            else if (_Map.TryGetValue(element.ParentID, out var parent))
            {
                var count = parent.Children.Count;
                parent.Children.ShiftUp(element);
                for (var i = 0; i < count; i++)
                {
                    var item = parent.Children[i];
                    if (item.Index != i)
                    {
                        item.Index = i;
                        _Engine_Cache.UpdateCache(item);
                    }
                }
            }
        }

        private async void DoRenameGeographyCommand(InstanceCache target)
        {
            if (target is null)
            {
                return;
            }

            var r = await SingleText("新建地理设定", "地理设定的名称", target.Name);

            if (!r.IsFinished)
            {
                return;
            }

            //
            // 
            target.Name = r.Value;
            _Engine_Cache.UpdateCache(target);
        }
        

        private void DoEditGeographyCommand(InstanceCache target)
        {
            if (target is null)
            {
                return;
            }

            var context = new PageContext
            {
                Id = target.Id,
            };

            //
            //
            context.Set(ContextBase.Key_Value, target);
        }


        private void DoShiftDownGeographyCommand(InstanceCache element)
        {
            if (element is null)
            {
                return;
            }

            if (string.IsNullOrEmpty(element.ParentID))
            {
                var count = Collection.Count;
                Collection.ShiftDown(element);

                for (var i = 0; i < count; i++)
                {
                    var item = Collection[i];
                    if (item.Index != i)
                    {
                        item.Index = i;
                        _Engine_Cache.UpdateCache(item);
                    }
                }
            }
            else if (_Map.TryGetValue(element.ParentID, out var parent))
            {
                var count = parent.Children.Count;
                parent.Children.ShiftDown(element);
                for (var i = 0; i < count; i++)
                {
                    var item = parent.Children[i];
                    if (item.Index != i)
                    {
                        item.Index = i;
                        _Engine_Cache.UpdateCache(item);
                    }
                }
            }
        }


        private void DoLockOrUnlockGeographyCommand(InstanceCache target)
        {
            if (target is null)
            {
                return;
            }


            target.IsLocked = !target.IsLocked;
            _Engine_Cache.UpdateCache(target);
            PushToView();
        }

        private async void DoRemoveGeographyCommand(InstanceCache target)
        {
            if (target is null)
            {
                return;
            }

            if (target.IsLocked)
            {
                await Warning(TitleSR.WarningOperation, TextSR.Remove_Item_Failed_Locked);
                return;
            }

            if (!await RemoveItem())
            {
                return;
            }

            //
            // TODO:
            _Map.Remove(target.Id);
            _Engine_Cache.RemoveCache(target, false);

            
            //
            // 
            if (string.IsNullOrEmpty(target.ParentID))
            {

                //
                //
                var index = Collection.IndexOf(target);

                //
                //
                Collection.Remove(target);

                if (target.Children is not null &&
                    target.Children.Count > 0)
                {
                    RemoveRootParent(index, target);
                }
                
                //
                // 更新顺序
                if (index < Collection.Count)
                {
                    for (var i = index; i < Collection.Count; i++)
                    {
                        var item = Collection[i];
                        item.Index = i;
                        _Engine_Cache.UpdateCache(item);
                    }
                }

                
            }
            else if (_Map.TryGetValue(target.ParentID, out var parent))
            {
                //
                //
                var index = parent.Children.IndexOf(target);

                parent.Children.Remove(target);
                
                //
                //
                if (target.Children.Count > 0)
                {
                
                    //
                    //
                    RemoveParent(index, target, parent);
                }
                
                //
                // 更新顺序
                if (index < parent.Children.Count)
                {
                    for (var i = index; i < parent.Children.Count; i++)
                    {
                        var item = parent.Children[i];
                        item.Index = i;
                        _Engine_Cache.UpdateCache(item);
                    }
                }
            }
        }
        
        private void RemoveRootParent(int index, InstanceCache target)
        {
            foreach (var item in target.Children)
            {
                //
                //
                item.ParentID = null;
                
                //
                //
                Collection.Insert(index, item);
                
                //
                //
                index++;
            }
        }

        private void RemoveParent(int index, InstanceCache target, InstanceCache parent)
        {
            foreach (var item in target.Children)
            {
                //
                //
                item.ParentID = null;
                
                //
                //
                parent.Children.Insert(index, item);
                
                //
                //
                index++;
            }
        }

        #endregion


        
        public string SearchBy
        {
            get => _searchBy;
            set => SetValue(ref _searchBy, value);
        }
        
        public string                              InstanceID => CurrentSelected?.Id;
        public ObservableCollection<InstanceCache> Collection { get; }
        
        
        public InstanceCache CurrentSelected
        {
            get => _currentSelected;
            set
            {
                SetValue(ref _currentSelected, value);
                PushToView();
            }
        }

        public ICommandEX AddGeographyCommand          { get; }
        public ICommandEX EditGeographyCommand         { get; }
        public ICommandEX RenameGeographyCommand       { get; }
        public ICommandEX ShiftUpGeographyCommand      { get; }
        public ICommandEX ShiftDownGeographyCommand    { get; }
        public ICommandEX LockOrUnlockGeographyCommand { get; }
        public ICommandEX OpenStoryCommand             { get; }
        public ICommandEX OpenNotionCommand            { get; }
        public ICommandEX RemoveGeographyCommand       { get; }
    }
}